package fix_window

import (
	"ratelimit"
	"sync/atomic"
	"time"
)

// 需要原子性地更新两个字段（f.startMs 和 f.requests），而每次只能更新其中之一。
//下面是一种可能的实现方式，将 f.startMs 和 f.requests 打包到一个结构体中，并用原子操作来更新它们
//type state struct {
//	startMs  int64
//	requests int64
//}
//
//type FixWindowLimit struct {
//	state      unsafe.Pointer
//	windowSize time.Duration
//	maxRequests int64
//}
//
//func New(windowSize time.Duration, maxRequests int64) *FixWindowLimit {
//	limiter := &FixWindowLimit{
//		windowSize:  windowSize,
//		maxRequests: maxRequests,
//	}
//	atomic.StorePointer(&limiter.state, unsafe.Pointer(&state{
//		startMs:  time.Now().UnixNano() / 1e6,
//		requests: 0,
//	}))
//	return limiter
//}
//
//func (f *FixWindowLimit) Allow() bool {
//	now := time.Now().UnixNano() / 1e6
//
//	for {
//		s := (*state)(atomic.LoadPointer(&f.state))
//
//		if now < s.startMs+int64(f.windowSize/time.Millisecond) {
//			if s.requests >= f.maxRequests {
//				return false
//			}
//
//			newState := &state{
//				startMs:  s.startMs,
//				requests: s.requests + 1,
//			}
//			if atomic.CompareAndSwapPointer(&f.state, unsafe.Pointer(s), unsafe.Pointer(newState)) {
//				return true
//			}
//
//		} else {
//			newState := &state{
//				startMs:  now,
//				requests: 1,
//			}
//			if atomic.CompareAndSwapPointer(&f.state, unsafe.Pointer(s), unsafe.Pointer(newState)) {
//				return true
//			}
//		}
//	}
//}

type FixWindowLimit struct {
	requests    int64
	startMs     int64
	windowSize  time.Duration
	maxRequests int64
}

func New(windowSize time.Duration, maxRequests int64) ratelimit.Limiter {
	limiter := &FixWindowLimit{
		startMs:     time.Now().UnixNano() / 1e6,
		windowSize:  windowSize,
		maxRequests: maxRequests,
	}

	return limiter
}

func (f *FixWindowLimit) Allow() bool {
	now := time.Now().UnixNano() / 1e6
	//timestamp := atomic.LoadInt64(&f.startMs)
	//requests := atomic.LoadInt64(&f.requests)
	//if now > timestamp+int64(f.windowSize/time.Millisecond) {
	//	if atomic.CompareAndSwapInt64(&f.startMs, timestamp, now) {
	//		atomic.CompareAndSwapInt64(&f.requests, requests, 0)
	//	}
	//}
	//
	//if atomic.AddInt64(&f.requests, 1) > f.maxRequests {
	//	return false
	//}
	//return true
	for {
		timestamp := atomic.LoadInt64(&f.startMs)
		requests := atomic.LoadInt64(&f.requests)

		if now > timestamp+int64(f.windowSize/time.Millisecond) {
			if atomic.CompareAndSwapInt64(&f.startMs, timestamp, now) && atomic.CompareAndSwapInt64(&f.requests, requests, 1) {
				return true
			}
		} else if requests < f.maxRequests-1 && atomic.CompareAndSwapInt64(&f.requests, requests, requests+1) {
			return true
		} else {
			return false
		}
	}
}
